﻿Public Class clsBlob

    Public Enum enumObjectType
        Null
        File
        TimeSeries 'contains pairs of offset seconds (integer) & values (single), so can be irregularly spaced
        ValueSeries 'contains time increment (integer) & values (single), so must be evenly spaced
    End Enum

    Public Shared Function ObjectType(ObjectName As String) As enumObjectType
        Select Case ObjectName.ToUpper
            Case "" : Return enumObjectType.Null
            Case "TMSER" : Return enumObjectType.TimeSeries
            Case "VLSER" : Return enumObjectType.ValueSeries
            Case Else : Return enumObjectType.File
        End Select
    End Function

    Public Shared Function ObjectName(ObjectType As enumObjectType) As String
        Select Case ObjectType
            Case enumObjectType.TimeSeries : Return "TMSER"
            Case enumObjectType.ValueSeries : Return "VLSER"
            Case Else : Return ""
        End Select
    End Function

    ''' <summary>
    ''' Save data to standard file 
    ''' </summary>
    ''' <param name="ObjectType">Type of object to save</param>
    ''' <param name="FileName">Name of file (for series types file extension will always be changed to assure it is .txt and passed back)</param>
    ''' <param name="Data">File contents or series binary data</param>
    ''' <param name="BaseDate">Reference data for computation of offsets; if equal to Date.MinValue is assumed to be null and will skip save</param>
    Public Shared Sub SaveBlob(ObjectType As enumObjectType, ByRef FileName As String, Data() As Byte, Optional BaseDate As Date = #12:00:00 PM#)
        Select Case ObjectType
            Case enumObjectType.File
                My.Computer.FileSystem.WriteAllBytes(FileName, Data, False)
            Case Else
                If BaseDate = Date.MinValue Then Exit Sub
                FileName = IO.Path.ChangeExtension(FileName, "txt")
                Dim swTimeSeries As New IO.StreamWriter(FileName)
                swTimeSeries.WriteLine("Date_Time" & vbTab & "Result")
                Dim NumRec As Integer = 0
                Select Case ObjectType
                    Case enumObjectType.TimeSeries
                        NumRec = Data.Length / 8
                        For i As Integer = 0 To NumRec - 1
                            Dim ElapsedSec As Integer = BitConverter.ToInt32(Data, i * 8)
                            Dim Result As Single = BitConverter.ToSingle(Data, i * 8 + 4)
                            swTimeSeries.WriteLine(BaseDate.AddSeconds(ElapsedSec).ToString("yyyy-MM-dd HH:mm:ss") & vbTab & Result)
                        Next
                    Case enumObjectType.ValueSeries
                        NumRec = Data.Length / 4 - 1
                        Dim DeltaSec As Integer = BitConverter.ToInt32(Data, 0)
                        For i As Integer = 0 To NumRec - 1
                            Dim Result As Single = BitConverter.ToSingle(Data, i * 4 + 4)
                            swTimeSeries.WriteLine(BaseDate.AddSeconds(DeltaSec * i).ToString("yyyy-MM-dd HH:mm:ss") & vbTab & Result)
                        Next
                End Select
                swTimeSeries.Close()
                swTimeSeries.Dispose()
        End Select
    End Sub

    ''' <summary>
    ''' Save data to standard file 
    ''' </summary>
    ''' <param name="ObjectName">Name of object to save</param>
    ''' <param name="FileName">Name of file (for series types file extension will always be changed to assure it is .txt and passed back)</param>
    ''' <param name="Data">File contents or series binary data</param>
    ''' <param name="BaseDate">Reference data for computation of offsets; if equal to Date.MinValue is assumed to be null and will skip save</param>
    Public Shared Sub SaveBlob(ObjectName As String, ByRef FileName As String, Data() As Byte, Optional BaseDate As Date = #12:00:00 PM#)
        SaveBlob(ObjectType(ObjectName), FileName, Data, BaseDate)
    End Sub

    ''' <summary>
    ''' Load data from standard file so can attach to table
    ''' </summary>
    ''' <param name="ObjectType">Type of object to load (note: if valueseries was attempted by reverted to timeseries, then will pass back revised type)</param>
    ''' <param name="FileName">Name of file (if series type file extension will always be changed to assure it is .txt)</param>
    ''' <param name="BaseDate">If series type, reference date for computation of offsets</param>
    ''' <param name="NumRec">If series type, number of records loaded</param>
    ''' <returns>Array of bytes (or Nothing if file does not exist)</returns>
    Public Shared Function LoadBlob(ByRef ObjectType As enumObjectType, FileName As String, Optional BaseDate As Date = #12:00:00 PM#, Optional ByRef NumRec As Integer = 0) As Byte()
        Select Case ObjectType
            Case enumObjectType.Null
                Return Nothing
            Case enumObjectType.File
                Return My.Computer.FileSystem.ReadAllBytes(FileName)
            Case Else
                FileName = IO.Path.ChangeExtension(FileName, "txt")
                If My.Computer.FileSystem.FileExists(FileName) Then
                    Dim Text As String = My.Computer.FileSystem.ReadAllText(FileName)
                    Text = Text.Replace(vbCrLf, vbCr).Replace(",", vbTab)
                    Dim arLine() As String = Text.Split(vbCr)
                    NumRec = arLine.Length - 2
                    Dim arByte() As Byte = Nothing
                    Select Case ObjectType
                        Case enumObjectType.TimeSeries
                            ReDim arByte(NumRec * 8 - 1)
                        Case enumObjectType.ValueSeries
                            ReDim arByte(4 + NumRec * 4 - 1)
                    End Select

                    Dim DeltaSec As Integer = 0
                    Dim PriorDate As Date = Date.MinValue

                    For r As Integer = 1 To arLine.Length - 2 'skip header line and split causes one extra item at end
                        Dim arItem() As String = arLine(r).Split(vbTab)
                        If arItem.Length <> 2 OrElse Not IsDate(arItem(0)) OrElse Not IsNumeric(arItem(1)) Then
                            Throw New Exception("Invalid time series data: " & arLine(r))
                            Return Nothing
                        Else
                            Select Case ObjectType
                                Case enumObjectType.TimeSeries
                                    Dim ElapsedSec As Integer = CDate(arItem(0)).Subtract(BaseDate).TotalSeconds
                                    Dim Result As Single = Val(arItem(1))
                                    Array.Copy(BitConverter.GetBytes(ElapsedSec), 0, arByte, (r - 1) * 8, 4)
                                    Array.Copy(BitConverter.GetBytes(Result), 0, arByte, (r - 1) * 8 + 4, 4)
                                Case enumObjectType.ValueSeries
                                    Dim ElapsedSec As Integer = CDate(arItem(0)).Subtract(BaseDate).TotalSeconds
                                    Dim Result As Single = Val(arItem(1))
                                    If r = 1 Then
                                    ElseIf r = 2 Then
                                        DeltaSec = CDate(arItem(0)).Subtract(PriorDate).TotalSeconds
                                        Array.Copy(BitConverter.GetBytes(ElapsedSec), 0, arByte, 0, 4)
                                    Else
                                        If CDate(arItem(0)).Subtract(PriorDate).TotalSeconds <> DeltaSec Then
                                            'caller thought it was a constant-step series, but it wasn't; reimport using variable step series
                                            'free some memory
                                            Text = ""
                                            ReDim arLine(0)
                                            ReDim arItem(0)
                                            ObjectType = enumObjectType.TimeSeries
                                            Return LoadBlob(ObjectType, FileName, BaseDate, NumRec)
                                        End If
                                    End If
                                    Array.Copy(BitConverter.GetBytes(Result), 0, arByte, 4 + (r - 1) * 4, 4)
                                    PriorDate = CDate(arItem(0))
                            End Select
                        End If
                    Next
                    Return arByte
                Else
                    Return Nothing
                End If
        End Select
    End Function
End Class
